using System.Diagnostics.CodeAnalysis;
using TUnit.Core.Interfaces;

namespace TUnit.Core.Helpers;

/// <summary>
/// Helper class for handling ClassConstructor attribute logic in a centralized way
/// </summary>
public static class ClassConstructorHelper
{
    /// <summary>
    /// Checks if a ClassConstructor attribute is present and uses it to create an instance
    /// </summary>
    /// <param name="attributes">The attributes to check</param>
    /// <param name="testClassType">The type of the test class to create</param>
    /// <param name="testSessionId">The test session ID</param>
    /// <param name="testContext">The test context</param>
    /// <returns>The created instance, or null if no ClassConstructor attribute is found</returns>
    public static async Task<object?> TryCreateInstanceWithClassConstructor(
        IReadOnlyList<Attribute> attributes,
        [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] Type testClassType,
        string testSessionId,
        TestContext testContext)
    {
        return await TryCreateInstanceWithClassConstructor(
            attributes,
            testClassType,
            TestBuilderContext.FromTestContext(testContext, attributes.OfType<IDataSourceAttribute>().FirstOrDefault()),
            testSessionId);
    }

    /// <summary>
    /// Checks if a ClassConstructor attribute is present and uses it to create an instance
    /// </summary>
    /// <param name="attributes">The attributes to check</param>
    /// <param name="testClassType">The type of the test class to create</param>
    /// <param name="testSessionId">The test session ID</param>
    /// <param name="testBuilderContext">The testBuilderContext</param>
    /// <returns>The created instance, or null if no ClassConstructor attribute is found</returns>
    public static async Task<object?> TryCreateInstanceWithClassConstructor(
        IReadOnlyList<Attribute> attributes,
        [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] Type testClassType,
        TestBuilderContext testBuilderContext,
        string testSessionId)
    {
        var classConstructorAttribute = attributes.OfType<ClassConstructorAttribute>().FirstOrDefault();

        if (classConstructorAttribute == null)
        {
            return null;
        }

        // Reuse existing ClassConstructor if already set, otherwise create new instance
        var classConstructor = testBuilderContext.ClassConstructor
            ?? (IClassConstructor)Activator.CreateInstance(classConstructorAttribute.ClassConstructorType)!;

        testBuilderContext.ClassConstructor = classConstructor;

        var classConstructorMetadata = new ClassConstructorMetadata
        {
            TestSessionId = testSessionId,
            TestBuilderContext = testBuilderContext
        };

        return await classConstructor.Create(testClassType, classConstructorMetadata);
    }

    /// <summary>
    /// Checks if the given attributes contain a ClassConstructor attribute
    /// </summary>
    public static bool HasClassConstructorAttribute(Attribute[] attributes)
    {
        return attributes.OfType<ClassConstructorAttribute>().Any();
    }

    /// <summary>
    /// Gets the ClassConstructor attribute from the given attributes, if present
    /// </summary>
    public static ClassConstructorAttribute? GetClassConstructorAttribute(Attribute[] attributes)
    {
        return attributes.OfType<ClassConstructorAttribute>().FirstOrDefault();
    }
}
